/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package MyEngine;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Random;
import Figures.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * simply chess engine based on mini-max algorithm with alfa-beta cuts
 * @author Macias
 */
public class ABEngine {

 final int SEARCH_DEPTH =3;
 final int INIT_NOTE = 9999;
  int NR_OF_THREADS =2;


 char[][]globalBoard = new char[8][8];
 ArrayList<Figures> figures;
 Point[][] moves;// = new Point[2][2];

 int[] finalAddMove;
 boolean[] threadFinish;
 int[][] addMoves;
 Node root;
 int isWhite;
 boolean isWhiteMove;
 boolean[] castles=new boolean[4];

 int iloscLisci;
 int counta,countb,count1,count2,countAll;

 boolean thereIsKing;

 Neural neural;// = new Neural();

 public ABEngine(){
    neural = new Neural(isWhiteMove);
 }
/**
 * function whcich returns the best move for selected player (white/black) in current situation on board
 *
 * @param b actual board
 * @param isWhiteMove  if it is white player move
 * @param canCastles  list of booleans which are saying which castle is possible (for both players)
 * @param lastMove  previous move of oponent
 * @return finalMove - best move in current situation - list of two points from, to 
 */
 public Point[] nextMove(char[][] b ,boolean isWhiteMove,boolean[] canCastles,Point[] lastMove){

     this.isWhiteMove=isWhiteMove;
     count1=count2=counta=countb=0;
     int nrOfThreads= NR_OF_THREADS;
     castles=canCastles;
     thereIsKing=false;
     //neural = new Neural(isWhiteMove);

//     char[][]board;
     Point[] finalMove = new Point[2];
     finalAddMove = new int[5];

     double firstCheck = neural.isChecked(b, true);
     boolean wasChecked=false;

     if(isWhiteMove){
        switch((int)firstCheck){
//            case -100 : finalAddMove[0]=-1;
//                        break;
            case 100: finalAddMove[0]=10;
                        wasChecked=true;
                        return null;
            case -1000: finalAddMove[0]=-10;
                        return null;
            case 1000: finalAddMove[0]=10;
            default:
                finalAddMove[0] = finalAddMove[0];
                        wasChecked=true;
                    break;
        }
    }
    else{
        switch((int)firstCheck){
            case -100 : finalAddMove[0]=10;
                        return null;
//            case 100: finalAddMove[0]=-1;
//                       break;
            case -1000: finalAddMove[0]=10;
                        return null;
            case 1000: finalAddMove[0]=-10;
            default:
                finalAddMove[0] = finalAddMove[0];
                    break;
        }
    }
    
//    board = new char[8][8];
    for(int c = 0;c<=7; c++)
     for(int r = 0;r<=7; r++){
//        board[c][r]=b[c][r];
        globalBoard[c][r] = b[c][r];
      }

     figures = checkMyFigures(b,isWhiteMove,castles);
     
    root = new Node();
    double bestNote = -INIT_NOTE;

  
    while(figures.size()<nrOfThreads) nrOfThreads--;
    
    threadFinish=new boolean[nrOfThreads];
    moves = new Point[nrOfThreads][2];
    addMoves = new int[nrOfThreads][5];
     for(int t=0;t<nrOfThreads;t++){
         threadFinish[t]=false;
     }
    Runnable[] workers = new Runnable[nrOfThreads];
    Thread[] threads = new Thread[nrOfThreads];

    for(int t=0;t<nrOfThreads;t++){
        workers[t]=new Worker(this,(t*figures.size()/nrOfThreads),((t+1)*figures.size()/nrOfThreads),t,lastMove);
        threads[t] = new Thread(workers[t]);
        threads[t].start();
    }

    boolean stop=false;
    while(!stop){
        int check=0;
        for(int t=0;t<nrOfThreads;t++){
             if(threadFinish[t]==false)break;
             check++;
        }
        if(check>=nrOfThreads)stop=true;

            try {
                Thread.sleep(10);
            } catch (InterruptedException ex) {
                Logger.getLogger(ABEngine.class.getName()).log(Level.SEVERE, null, ex);
            }
    }
    int nulls=0;
//    System.out.println("threads: "+nrOfThreads);
    for(int t=0;t<nrOfThreads;t++){
        if(moves[t][0] !=null){
                ArrayList<int[]> move =new ArrayList<int[]>();
            int[] tmp = new int[4];
                  tmp[0] = moves[t][0].x;
                  tmp[1] = moves[t][0].y;
                  tmp[2] = moves[t][1].x;
                  tmp[3] = moves[t][1].y;
            move.add(tmp);
            double note =calcNote(getActualBoard(move));
            if(isWhiteMove)note=note*(-1);
            //if we are not running away from check then make this move less attractive
            double check = neural.isChecked(this.getActualBoard(move), true);
            boolean accept = this.isMoveAccepted(check,isWhiteMove);
            System.out.println("przed best: "+bestNote + " note: "+note+ " move: "+move);
            if(!accept){
                note-=100;
            }

            System.out.println("po best: "+bestNote + " note: "+note+ " move: "+move);

            if(bestNote<note){
                //#############check situation - akceptuejmy tylko te ruchy ktore uciekaja z szacha
//                double check = neural.isChecked(this.getActualBoard(move), true);
//                boolean accept = this.isMoveAccepted(check,isWhiteMove);
               
                    finalMove = moves[t];
                    bestNote=note;
                    finalAddMove = addMoves[t];
                
            }
        }
        else{
            nulls++;
        }
    }

    if(finalMove==null) finalMove=moves[0];

    if(nulls==nrOfThreads){
        finalMove=null;
        finalAddMove[0]=-1;
        finalAddMove[1]=0;
        finalAddMove[2]=0;
        finalAddMove[3]=0;
        finalAddMove[4]=0;
    }
   
    

    ArrayList<int[]> list = new ArrayList();;
    int[] tmp = new int[4];
    tmp[0]=finalMove[0].x;
    tmp[1]=finalMove[0].y;
    tmp[2]=finalMove[1].x;
    tmp[3]=finalMove[1].y;
    list.add(tmp);
    double isCheck = neural.isChecked(getActualBoard(list),true);
   
    if(isWhiteMove){
        switch((int)isCheck){
            case -100 : finalAddMove[0]=-10;
                        break;
            case 100: finalAddMove[0]=1;
                        break;
            case -1000: finalAddMove[0]=-10;
                        break;
            case 1000: finalAddMove[0]=10;
            default:
                finalAddMove[0] = finalAddMove[0];
                    break;
        }
    }
    else{
        switch((int)isCheck){
            case -100 : finalAddMove[0]=1;
                        break;
            case 100: finalAddMove[0]=-10;
                        break;
            case -1000: finalAddMove[0]=10;
                        break;
            case 1000: finalAddMove[0]=-10;
            default:
                finalAddMove[0] = finalAddMove[0];
                    break;
        }
    }
    if(!thereIsKing)finalAddMove[0]=-10;
    if(finalMove!=null){
      System.out.println("bialy: "+this.isWhiteMove +" ruch: z "+finalMove[0].x +","+finalMove[0].y + " na: "+finalMove[1].x+","+finalMove[1].y);
      System.out.println("typ ruchu: "+finalAddMove[0]);
      System.out.println("ocena: "+bestNote);
     }
    else System.out.println("przegrales");

    return finalMove;
    }

/**
 * Function which returns list of all figures for chosen player
 * @param board  actual board
 * @param isWhiteMove  if it is white player move
 * @param canMakeCastles  list of booleans which are saying which castle is possible (for both players)
 * @return list of figures
 */
private ArrayList<Figures> checkMyFigures(char[][] board,boolean isWhiteMove,boolean[] canMakeCastles) {

        ArrayList<Figures> myFigures = new ArrayList();

        if(isWhiteMove){                          //if its white's move we are looking for capital letters
            for(int c = 0;c<=7; c++)
                for(int r = 0;r<=7; r++){
//                    char s = board[c][r];
//                    System.out.println(""+ s);
                    switch(board[c][r]){
                        case 'N': myFigures.add(new Knight(true, new Point(c,r)));
                                        break;
                        case 'B': myFigures.add(new Bishop(true,new Point(c,r)));
                                        break;
                        case 'P': myFigures.add(new Pawn(true,new Point(c,r)));
                                        break;
                        case 'K':  myFigures.add(new King(true,new Point(c,r),canMakeCastles[0],canMakeCastles[1]));
                                    thereIsKing=true;
                                        break;
                        case 'R': myFigures.add(new Rouge(true,new Point(c,r)));
                                        break;
                        case 'Q': myFigures.add(new Queen(true,new Point(c,r)));
                                        break;
                    }
                }
        }
        else{
             for(int c = 0;c<=7; c++)
                for(int r = 0;r<=7; r++){
                    switch(board[c][r]){
                        case 'n': myFigures.add(new Knight(false, new Point(c,r)));
                                        break;
                        case 'b': myFigures.add(new Bishop(false,new Point(c,r)));
                                        break;
                        case 'p': myFigures.add(new Pawn(false,new Point(c,r)));
                                        break;
                        case 'k': myFigures.add(new King(false,new Point(c,r),canMakeCastles[2],canMakeCastles[3]));
                                    thereIsKing=true;
                                        break;
                        case 'r': myFigures.add(new Rouge(false,new Point(c,r)));
                                        break;
                        case 'q': myFigures.add(new Queen(false,new Point(c,r)));
                                        break;
                    }
                }
        }
        return myFigures;
    }
/**
 * Function which is judging if the board is good or not (calculating note) using Neural Network
 * @param tmpBoard - board to be judged
 * @return n - note of board
 */
  private double calcNote(char[][] tmpBoard) {
        Random rand = new Random();
        int r  = rand.nextInt(100);

        count2++;
//        System.out.println("ocena "+r);
//                      //drugokowanie tablicy
//              for(int row=0;row<board.length;row++){
//                for(int c=0;c<board[0].length;c++){
//                    System.out.print(""+board[c][row]);
//                }
//                System.out.print("\n");
//                }
//              System.out.println("");
//              //koniec drukowania
       double n= neural.getNote(tmpBoard,isWhiteMove);
//       n=n+r;

        return n;

    }
/**
 *Function which generates actual board - executing every move which is already done
 * @param moves  list of moves already done
 * @return board - actual board
 */
   public char[][] getActualBoard( ArrayList<int[]> moves) {
        char[][] board = new char[8][8];
        char[][] exBoard = {{'r','n','b','q','k','p','0'},
                            {'R','N','B','Q','K','P','0'}};
        for(int c = 0;c<=7; c++)
        for(int r = 0;r<=7; r++)board[c][r]=globalBoard[c][r];
        for(int m=0;m<moves.size();m++){
            if(moves.get(m)[1]<0){
                char figureType = exBoard[moves.get(m)[1]*(-1)-1][moves.get(m)[0]];
                 board[moves.get(m)[2]][moves.get(m)[3]]=figureType;
                 continue;
            }
            else{
                   char figureType = board[moves.get(m)[0]][moves.get(m)[1]];
                if(figureType == 'K'){
                    castles[0]=false;
                    castles[1]=false;
                }
                if(figureType == 'k'){
                    castles[2]=false;
                    castles[3]=false;
                }
                if(figureType == 'r') {
                    if(moves.get(m)[0]==0) castles[2]=false;
                    else castles[3]=false;
                }
                  if(figureType == 'R') {
                    if(moves.get(m)[0]==0) castles[0]=false;
                    else castles[1]=false;
                }
//System.out.println(""+moves.get(m)[0] + " , "+moves.get(m)[1] + " , "+moves.get(m)[2] + " , "+moves.get(m)[3] + " , ");
                board[moves.get(m)[0]][moves.get(m)[1]]='0'; //we clear old location
//                System.out.println("0: "+moves.get(m)[0] + " 1: "+moves.get(m)[1]+ " 2: "+moves.get(m)[2] + " 3: "+moves.get(m)[3] );
                board[moves.get(m)[2]][moves.get(m)[3]]=figureType;
    //            if(moves.get(m)[3] == ){}
            }

        }
        return board;
    }
/**
 * function which returns type of move : 0 -normal move; 1-player wins; -1-opponent wins
 * 2-castle; 3-beat in passage; 4- promotion of pawn
 *
 * if type of move is not equal to 0 function returns additional move which has to be executed
 *
 * @return finalAddMove[0] - type of move
 * finalMove[1] - additional move : from.x
 * finalMove[2] - additional move : from.y
 * finalMove[3] - additional move : to.x
 * finalMove[4] - additional move : to.y
 */
    public int[] getMoveType(){
        return finalAddMove;
    }

    public void trainNetwork(){
        neural.doucz();
    }

    public int ifIsChecked(char [][] board, boolean spr){
        int n = neural.isChecked(board, spr);
        System.out.println(" "+n);
        return n;
    }

    private boolean isMoveAccepted(double check, boolean whiteMove) {
        if(whiteMove){
        switch((int)check){
            case -100 : return false;
            case 100: return true;
            case -1000: return false;
            case 1000: return true;
            default:
                    break;
        }
        }
        else{
            switch((int)check){
                case -100 : return true;
                case 100: return false;
                case -1000: return true;
                case 1000: return false;
                default:
                        break;
            }
        }
        return true;
    }

}
